﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

using OpenSunTracker.RestClient.Extensions;
using System.ComponentModel;
using System.Reflection;

namespace OpenSunTracker.RestClient
{
    public class OpenSunTrackerClient : INotifyPropertyChanged
    {
        #region Consts&Fields
        private const int C_INT_MAX_MESSAGES = 10;
        #endregion

        #region CTOR

        public OpenSunTrackerClient()
        {
            Logs = new ObservableCollection<LogEntry>();
            Messages = new ObservableCollection<Message>();
        }

        #endregion

        #region Properties

        string _url = string.Empty;
        public string Url
        {
            get { return _url; }
            set
            {
                if (String.IsNullOrEmpty(value)) throw new OSTException("new Url not valid");
                _url = value;
                Reset();

                AddMessage("url changed2: " + _url);
            }
        }
        public bool HasUrl
        {
            get
            {
                return (string.IsNullOrEmpty(Url)) ? false : true;
            }
        }

        public ObservableCollection<Message> Messages { get; private set; }

        public ObservableCollection<LogEntry> Logs { get; private set; }
        public bool HasLogs { get { return (Logs != null && Logs.Count > 0) ? true : false; } }

        public Configuration Config { get; private set; }
        public bool HasConfig { get { return (Config == null) ? false : true; } }

        public TrackingDay DayTracking { get; private set; }
        public bool HasDayTracking { get { return (DayTracking == null) ? false : true; } }

        public SystemState State { get; set; }
        public bool HasState { get { return (State == null) ? false : true; } }

        #endregion

        #region Methods

        private void Reset()
        {
            Logs.Clear();
            Config = null;
            DayTracking = null;
            State = null;
        }

        private void AssertIfNoUrlIsAvailable()
        {
            if (!HasUrl)
            {
                throw new OSTException("no Url available or not connected");
            }
        }

        public void UpdateLogs()
        {
            AssertIfNoUrlIsAvailable();

            string rssUri = Url + "/rss";
            var logs = rssUri.ReadRss();
            if (Logs.Count == 0)
            {
                foreach (var newLog in logs)
                {
                    Logs.Add(newLog);
                }
            }
            else
            {
                foreach (var newLog in logs)
                {
                    if (!Try2FindLogEntry(newLog))
                    {
                        Logs.Insert(0, newLog);
                    }
                }
            }

            AddMessage("logs updated");
        }

        private bool Try2FindLogEntry(LogEntry newLog)
        {
            foreach (var oldLog in Logs)
            {
                if (oldLog.Title.Equals(newLog.Title)
                    && oldLog.PublicationDate.Equals(newLog.PublicationDate))
                {
                    return true;
                }
            }
            return false;
        }

        public void UpdateConfiguration()
        {
            AssertIfNoUrlIsAvailable();

            string configUri = Url + "/configuration";
            var resp = configUri.ExecuteReadRequest();

            string configXml = resp.ReadAllText();
            Config = configXml.ToTypeFromXmlString<Configuration>();

            var assMajorVersionValue = Assembly.GetExecutingAssembly().GetName().Version.Major;

            if (assMajorVersionValue != Config.Vers)
                throw new OSTException("config-version incompatible, expected '"
                    + assMajorVersionValue + "', real '" + Config.Vers + "'");

            AddMessage("configuration updated");
            OnNotifyPropertyChanged("Config");
        }

        public void UpdateState()
        {
            AssertIfNoUrlIsAvailable();

            var newState = new SystemState();

            newState.Date = (Url + "/date").ExecuteReadRequest().ReadAllText();
            newState.Uptime = (Url + "/system/uptime").ExecuteReadRequest().ReadAllText();
            newState.Version = (Url + "/system/version").ExecuteReadRequest().ReadAllText();

            newState.FreeBytes = Int32.Parse((Url + "/system/gc").ExecuteReadRequest().ReadAllText());

            newState.WindSpeed = (Url + "/wind").ExecuteReadRequest().ReadAllText();
            newState.WindSpeedAverage = (Url + "/wind/average").ExecuteReadRequest().ReadAllText();
            try
            {
                newState.MotorState = "...all fine";
                newState.MotorPosition = Int32.Parse((Url + "/motor").ExecuteReadRequest().ReadAllText());
            }
            catch (Exception ex)
            {
                newState.MotorState = "error: " + ex.Message;
                AddMessage(newState.MotorState);
            }

            try
            {
                newState.TrackingState = "...all fine";
                var nextActionXml = (Url + "/tracker/next").ExecuteReadRequest().ReadAllText();
                newState.NextTrackingAction = nextActionXml.ToTypeFromXmlString<TrackingAction>();

                newState.TrackingWindAlarm = (Url + "/tracker/windalarm").ExecuteReadRequest().ReadAllText();
            }
            catch (Exception ex)
            {
                newState.TrackingState = "error: " + ex.Message;
                AddMessage(newState.TrackingState);
            }

            State = newState;

            AddMessage("state updated");

            OnNotifyPropertyChanged("State");
        }

        public void AddMessage(string msg)
        {
            Messages.Insert(0, new Message() { Timestamp = DateTime.Now, MessageText = msg });

            if (Messages.Count > C_INT_MAX_MESSAGES)
            {
                Messages.RemoveAt(Messages.Count - 1);
            }

            OnNotifyPropertyChanged("Messages");
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// notifies clients over changes in puplic properties
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnNotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null && !string.IsNullOrEmpty(propertyName))
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

    }
}
